<?php
class system_controller_request_http extends system_controller_request_abstract {
	const SCHEME_HTTPS = 'https';
	const SCHEME_HTTP = 'http';
	
	public function __construct() {
		$this->setRequestUri();
	}
	
	public function __get($key) {
		switch(true) {
			case isset($this->_params[$key]):
				return $this->_params[$key];
			case isset($_GET[$key]):
				return $_GET[$key];
			case isset($_POST[$key]):
				return $_POST[$key];
			case isset($_COOKIE[$key]):
				return $_COOKIE[$key];
			case ($key == 'REQUEST_URI'):
				return $this->getRequestUri();
			case ($key == 'PATH_INFO'):
				return $this->getPathInfo();
			case isset($_SERVER[$key]):
				return $_SERVER[$key];
			case isset($_ENV[$key]):
				return $_ENV[$key];
			default:
				return null;
		}
	}
	
	public function get($key) {
		return $this->__get($key);
	}
	
	public function __set($key, $value) {
		throw new Exception('Setting values in superglobals not allowed; please use setParam()');
	}
	
	public function set($key, $value) {
		return $this->__set($key, $value);
	}
	
	public function __isset($key) {
		switch(true) {
			case isset($this->_params[$key]):
				return true;
			case isset($_GET[$key]):
				return true;
			case isset($_POST[$key]):
				return true;
			case isset($_COOKIE[$key]):
				return true;
			case isset($_SERVER[$key]):
				return true;
			case isset($_ENV[$key]):
				return true;
			default:
				return false;
		}
	}
	
	public function has($key) {
		return $this->__isset($key);
	}
	
	public function setQuery($spec, $value = null) {
		if((null === $value) && !is_array($spec)) {
			throw new Exception('Invalid value passed to setQuery(); must be either array of values or key/value pair');
		}
		if((null === $value) && is_array($spec)) {
			foreach($spec as $key => $value) {
				$this->setQuery($key, $value);
			}
			return $this;
		}
		$_GET[(string) $spec] = $value;
		return $this;
	}
	
	/**
	 * 得到请求$_GET数据
	 * @param $key
	 * @param $default
	 */
	public function getQuery($key = null, $default = null) {
		if(null === $key) {
			return $_GET;
		}
		
		return (isset($_GET[$key])) ? $_GET[$key] : $default;
	}
	
	public function setPost($spec, $value = null) {
		if((null === $value) && !is_array($spec)) {
			throw new Exception('Invalid value passed to setPost(); must be either array of values or key/value pair');
		}
		if((null === $value) && is_array($spec)) {
			foreach($spec as $key => $value) {
				$this->setPost($key, $value);
			}
			return $this;
		}
		$_POST[(string) $spec] = $value;
		return $this;
	}
	
	/**
	 * 得到请求$_POST数据
	 * @param unknown_type $key
	 * @param unknown_type $default
	 */
	public function getPost($key = null, $default = null) {
		if(null === $key) {
			return $_POST;
		}
		
		return (isset($_POST[$key])) ? $_POST[$key] : $default;
	}
	
	/**
	 * 得到用户的COOKIE数据
	 * @param $key
	 * @param $default
	 */
	public function getCookie($key = null, $default = null) {
		if(null === $key) {
			return $_COOKIE;
		}
		
		return (isset($_COOKIE[$key])) ? $_COOKIE[$key] : $default;
	}
	
	public function getServer($key = null, $default = null) {
		if(null === $key) {
			return $_SERVER;
		}
		
		return (isset($_SERVER[$key])) ? $_SERVER[$key] : $default;
	}
	
	public function getEnv($key = null, $default = null) {
		if(null === $key) {
			return $_ENV;
		}
		
		return (isset($_ENV[$key])) ? $_ENV[$key] : $default;
	}
	
	protected $_requestUri = null;
	public function setRequestUri() {
		if(isset($_SERVER['HTTP_X_REWRITE_URL'])) { // check this first so IIS will catch
			$requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
		} elseif(isset($_SERVER['IIS_WasUrlRewritten']) && $_SERVER['IIS_WasUrlRewritten'] == '1' && isset($_SERVER['UNENCODED_URL']) && $_SERVER['UNENCODED_URL'] != '') {
			$requestUri = $_SERVER['UNENCODED_URL'];
		} elseif(isset($_SERVER['REQUEST_URI'])) {
			$requestUri = $_SERVER['REQUEST_URI'];
			// http proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path
			$schemeAndHttpHost = $this->getScheme() . '://' . $this->getHttpHost();
			if(strpos($requestUri, $schemeAndHttpHost) === 0) {
				$requestUri = substr($requestUri, strlen($schemeAndHttpHost));
			}
		} elseif(isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0, PHP as CGI
			$requestUri = $_SERVER['ORIG_PATH_INFO'];
			if(!empty($_SERVER['QUERY_STRING'])) {
				$requestUri .= '?' . $_SERVER['QUERY_STRING'];
			}
		} else {
			return $this;
		}
		$this->_requestUri = $requestUri;
		return $this;
	}
	
	public function getRequestUri() {
		if(empty($this->_requestUri)) {
			$this->setRequestUri();
		}
		
		return $this->_requestUri;
	}
	
	protected $_baseUrl = null;
	public function setBaseUrl($baseUrl = null) {
		if((null !== $baseUrl) && !is_string($baseUrl)) {
			return $this;
		}
		
		if($baseUrl === null) {
			$filename = (isset($_SERVER['SCRIPT_FILENAME'])) ? basename($_SERVER['SCRIPT_FILENAME']) : '';
			
			if(isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $filename) {
				$baseUrl = $_SERVER['SCRIPT_NAME'];
			} elseif(isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $filename) {
				$baseUrl = $_SERVER['PHP_SELF'];
			} elseif(isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
				$baseUrl = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
			} else {
				// Backtrack up the script_filename to find the portion matching
				// php_self
				$path = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : '';
				$file = isset($_SERVER['SCRIPT_FILENAME']) ? $_SERVER['SCRIPT_FILENAME'] : '';
				$segs = explode('/', trim($file, '/'));
				$segs = array_reverse($segs);
				$index = 0;
				$last = count($segs);
				$baseUrl = '';
				do {
					$seg = $segs[$index];
					$baseUrl = '/' . $seg . $baseUrl;
					++$index;
				} while(($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
			}
			
			// Does the baseUrl have anything in common with the request_uri?
			$requestUri = $this->getRequestUri();
			
			if(0 === strpos($requestUri, $baseUrl)) {
				// full $baseUrl matches
				$this->_baseUrl = $baseUrl;
				return $this;
			}
			
			if(0 === strpos($requestUri, dirname($baseUrl))) {
				// directory portion of $baseUrl matches
				$this->_baseUrl = rtrim(dirname($baseUrl), '/');
				return $this;
			}
			
			$truncatedRequestUri = $requestUri;
			if(($pos = strpos($requestUri, '?')) !== false) {
				$truncatedRequestUri = substr($requestUri, 0, $pos);
			}
			
			$basename = basename($baseUrl);
			if(empty($basename) || !strpos($truncatedRequestUri, $basename)) {
				// no match whatsoever; set it blank
				$this->_baseUrl = '';
				return $this;
			}
			
			// If using mod_rewrite or ISAPI_Rewrite strip the script filename
			// out of baseUrl. $pos !== 0 makes sure it is not matching a value
			// from PATH_INFO or QUERY_STRING
			if((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) {
				$baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
			}
		}
		
		$this->_baseUrl = rtrim($baseUrl, '/');
		return $this;
	}
	
	public function getBaseUrl() {
		if(null === $this->_baseUrl) {
			$this->setBaseUrl();
		}
		
		return $this->_baseUrl;
	}
	
	public function setBasePath($basePath = null) {
		if($basePath === null) {
			$filename = (isset($_SERVER['SCRIPT_FILENAME'])) ? basename($_SERVER['SCRIPT_FILENAME']) : '';
			
			$baseUrl = $this->getBaseUrl();
			if(empty($baseUrl)) {
				$this->_basePath = '';
				return $this;
			}
			
			if(basename($baseUrl) === $filename) {
				$basePath = dirname($baseUrl);
			} else {
				$basePath = $baseUrl;
			}
		}
		
		if(substr(PHP_OS, 0, 3) === 'WIN') {
			$basePath = str_replace('\\', '/', $basePath);
		}
		
		$this->_basePath = rtrim($basePath, '/');
		return $this;
	}
	
	public function getBasePath() {
		if(null === $this->_basePath) {
			$this->setBasePath();
		}
		
		return $this->_basePath;
	}
	
	protected $_pathInfo = null;
	public function setPathInfo($pathInfo = null) {
		if($pathInfo === null) {
			$baseUrl = $this->getBaseUrl();
			
			if(null === ($requestUri = $this->getRequestUri())) {
				return $this;
			}
			
			// Remove the query string from REQUEST_URI
			if(!!($pos = strpos($requestUri, '?'))) {
				$requestUri = substr($requestUri, 0, $pos);
			}
			
			if(null !== $baseUrl && ((!empty($baseUrl) && 0 === strpos($requestUri, $baseUrl)) || empty($baseUrl)) && false === ($pathInfo = substr($requestUri, strlen($baseUrl)))) {
				
				// If substr() returns false then PATH_INFO is set to an empty string 
				$pathInfo = '';
			} elseif(null === $baseUrl || (!empty($baseUrl) && false === strpos($requestUri, $baseUrl))) {
				
				$pathInfo = $requestUri;
			}
		}
		
		$this->_pathInfo = (string) $pathInfo;
		return $this;
	}
	
	public function getPathInfo() {
		if(empty($this->_pathInfo) || $this->_pathInfo === null) {
			$this->setPathInfo();
		}
		
		return $this->_pathInfo;
	}
	protected $_paramSources = array();
	public function setParamSources(array $paramSources = array()) {
		$this->_paramSources = $paramSources;
		return $this;
	}
	
	public function getParamSources() {
		return $this->_paramSources;
	}
	
	public function setParam($key, $value) {
		$key = (null !== ($alias = $this->getAlias($key))) ? $alias : $key;
		parent::setParam($key, $value);
		return $this;
	}
	
	public function getParam($key, $default = null) {
		$keyName = (null !== ($alias = $this->getAlias($key))) ? $alias : $key;
		
		$paramSources = $this->getParamSources();
		if(isset($this->_params[$keyName])) {
			return $this->_params[$keyName];
		} elseif(in_array('_GET', $paramSources) && (isset($_GET[$keyName]))) {
			return $_GET[$keyName];
		} elseif(in_array('_POST', $paramSources) && (isset($_POST[$keyName]))) {
			return $_POST[$keyName];
		}
		
		return $default;
	}
	
	public function getParams() {
		$return = $this->_params;
		$paramSources = $this->getParamSources();
		if(in_array('_GET', $paramSources) && isset($_GET) && is_array($_GET)) {
			$return += $_GET;
		}
		if(in_array('_POST', $paramSources) && isset($_POST) && is_array($_POST)) {
			$return += $_POST;
		}
		return $return;
	}
	
	public function setParams(array $params) {
		foreach($params as $key => $value) {
			$this->setParam($key, $value);
		}
		return $this;
	}
	
	protected $_aliases = array();
	public function setAlias($name, $target) {
		$this->_aliases[$name] = $target;
		return $this;
	}
	
	public function getAlias($name) {
		if(isset($this->_aliases[$name])) {
			return $this->_aliases[$name];
		}
		
		return null;
	}
	
	public function getAliases() {
		return $this->_aliases;
	}
	
	public function getMethod() {
		return $this->getServer('REQUEST_METHOD');
	}
	
	public function isPost() {
		if('POST' == $this->getMethod()) {
			return true;
		}
		
		return false;
	}
	
	public function isGet() {
		if('GET' == $this->getMethod()) {
			return true;
		}
		
		return false;
	}
	
	public function isPut() {
		if('PUT' == $this->getMethod()) {
			return true;
		}
		
		return false;
	}
	
	public function isDelete() {
		if('DELETE' == $this->getMethod()) {
			return true;
		}
		
		return false;
	}
	
	public function isHead() {
		if('HEAD' == $this->getMethod()) {
			return true;
		}
		
		return false;
	}
	
	public function isOptions() {
		if('OPTIONS' == $this->getMethod()) {
			return true;
		}
		
		return false;
	}
	
	public function isXmlHttpRequest() {
		return ($this->getHeader('X_REQUESTED_WITH') == 'XMLHttpRequest');
	}
	
	public function isFlashRequest() {
		$header = strtolower($this->getHeader('USER_AGENT'));
		return (strstr($header, ' flash')) ? true : false;
	}
	
	public function isSecure() {
		return ($this->getScheme() === self::SCHEME_HTTPS);
	}
	
	protected $_rawBody = null;
	public function getRawBody() {
		if(null === $this->_rawBody) {
			$body = file_get_contents('php://input');
			
			if(strlen(trim($body)) > 0) {
				$this->_rawBody = $body;
			} else {
				$this->_rawBody = false;
			}
		}
		return $this->_rawBody;
	}
	
	public function getHeader($header) {
		if(empty($header)) {
			throw new Exception('An HTTP header name is required');
		}
		
		// Try to get it from the $_SERVER array first
		$temp = 'HTTP_' . strtoupper(str_replace('-', '_', $header));
		if(!empty($_SERVER[$temp])) {
			return $_SERVER[$temp];
		}
		
		// This seems to be the only way to get the Authorization header on
		// Apache
		if(function_exists('apache_request_headers')) {
			$headers = apache_request_headers();
			if(!empty($headers[$header])) {
				return $headers[$header];
			}
		}
		
		return false;
	}
	
	public function getScheme() {
		return ($this->getServer('HTTPS') == 'on') ? self::SCHEME_HTTPS : self::SCHEME_HTTP;
	}
	public function getHttpHost() {
		$host = $this->getServer('HTTP_HOST');
		if(!empty($host)) {
			return $host;
		}
		
		$scheme = $this->getScheme();
		$name = $this->getServer('SERVER_NAME');
		$port = $this->getServer('SERVER_PORT');
		
		if(($scheme == self::SCHEME_HTTP && $port == 80) || ($scheme == self::SCHEME_HTTPS && $port == 443)) {
			return $name;
		} else {
			return $name . ':' . $port;
		}
	}
	
	public function getClientIp($checkProxy = true) {
		if($checkProxy && $this->getServer('HTTP_CLIENT_IP') != null) {
			$ip = $this->getServer('HTTP_CLIENT_IP');
		} else 
			if($checkProxy && $this->getServer('HTTP_X_FORWARDED_FOR') != null) {
				$ip = $this->getServer('HTTP_X_FORWARDED_FOR');
			} else {
				$ip = $this->getServer('REMOTE_ADDR');
			}
		return $ip;
	}
}